package com.veetao.api.filter;

import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.atomic.AtomicInteger;

import javax.servlet.http.HttpServletRequest;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.stereotype.Component;

import com.veetao.api.consts.HttpConstants;
import com.veetao.api.model.ApiResult;
import com.veetao.api.utils.ConfigUtils;
import com.veetao.api.utils.McpUtils;
import com.veetao.api.utils.RequestUtils;

@Component
public class MethodNameFrequencyChecker extends ApiRequestChecker {
	private static final Map<String, BlockChecker> mapBlockCheckers = new ConcurrentHashMap<String, BlockChecker>();
	/** 阻塞数警戒线 */
	private static final int warnBlockThreadsCount = ConfigUtils.warnBlockThreads();
	private static final Log blockingLogger = LogFactory.getLog("method.frequency");
	/** 两次阻塞日志间的最小时间 */
	private static final int blockLogPeriod = 60 * 1000;// 1分钟


	public MethodNameFrequencyChecker() {
		super();
		this.setOrder(2);
	}

	@Override
	public ApiResult check(HttpServletRequest request) {
			// 解析uri到method名字，优先判断
		String methodName = McpUtils.getCmdMethodFromURI(request.getRequestURI());
		if (methodName == null) {// URL中没有时，从参数中取一次容错。
			methodName = RequestUtils.get(request,HttpConstants.PARAM_METHOD);
		}
		

		BlockChecker checker = mapBlockCheckers.get(methodName);
		if (checker == null) {
			checker = new BlockChecker();
			mapBlockCheckers.put(methodName, checker);
			return SUCC_RESULT;
		}
		
		
		long interval = System.currentTimeMillis() - checker.lastWarnTime;
		if(interval < blockLogPeriod) {
			int blockCount = checker.blockCounter().incrementAndGet();
			if (blockCount > warnBlockThreadsCount) {// 达到警告线
				String causeMessage = String.format("%s method %s threads are blocking.", methodName, blockCount);
				blockingLogger.warn(causeMessage);
				
				checker.reset();
				
			} 
		} else {
			checker.blockCounter().decrementAndGet();
		}
		
		return SUCC_RESULT;
		
	}
	
	class BlockChecker {

		/** 最后一次警告日志时间 */
		private long lastWarnTime = System.currentTimeMillis();

		private final AtomicInteger blockedCounter = new AtomicInteger(0);

		public long getLastWarnTime() {
			return lastWarnTime;
		}

		public void reset() {
			this.lastWarnTime = System.currentTimeMillis();
			this.blockedCounter.set(0);
		}


		public AtomicInteger blockCounter() {
			return blockedCounter;
		}

	}

}
